package com.wmx.thread.concurrent.ForkJoin;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

/**********************************
 * @author wmx
 * @date 2019/9/3 9:49
 * @description:
 * @version 1.0
 * 需要将1000000000个数据分成若干组，分别求和，然后合并计算结果。所以属于上述的第二种fork+join用法。
 * 注意如何根据阈值THRESHOLD计算分组(fork())。
 * 注意递归fork()的用法。
 * 注意递归join()的用法。
 *
 ************************************/
@Slf4j
public class RecursiveTaskDemo {

    static class LargeSetComputeTask extends RecursiveTask<Long>{
        //阈值
        private static final int THRESHOLD = 100000;
        private int start;
        private int end ;
        LargeSetComputeTask(int start , int end){
            this.start =  start ;
            this.end = end ;
        }

        @Override
        protected Long compute() {
            if(end - start < THRESHOLD){
                return  computeByUnit();
            }else {
                //计算中间索引
                int middle = (start + end) / 2;
                //定义子任务-迭代思想
                LargeSetComputeTask left = new LargeSetComputeTask(start, middle);
                LargeSetComputeTask right = new LargeSetComputeTask(middle, end);
                //划分子任务-fork
                left.fork();
                right.fork();
                //合并计算结果-join
                return left.join() + right.join();
            }

        }

       // 计算小于阈值的数据
       public long computeByUnit(){
            long sum = 0L;
           for (int i = start; i <= end; i++) {
                 sum += i;
           }
           return sum;
       };




        public static void main(String[] args) throws Exception {
           //计算(0+1+2+3+1000000000)*2的结果
            int count = 1000000001;

            //第一种方式：单线程计算
            long start1 = System.currentTimeMillis();
            log.info("1.第一种计算方式--单线程计算");
            long result = 0L;
            for (long i = 0; i < count; i++) {
                result += i;
            }
            log.info("1.计算结果：" + result + ",用时：" + (System.currentTimeMillis() - start1) + "ms.\n");

            //通过ForkJoin框架进行子任务计算
            long start2 = System.currentTimeMillis();
            log.info("2.第二种计算方式--ForkJoin框架计算");
            //定义ForkJoinPool线程池
            ForkJoinPool forkJoinPool = new ForkJoinPool();
            //定义计算任务
            LargeSetComputeTask computeTask = new LargeSetComputeTask(0, count);
            //提交计算任务
            Future<Long> future = forkJoinPool.submit(computeTask);
            //执行完任务关闭线程池
            forkJoinPool.shutdown();
            //输出计算结果：
            log.info("2.计算结果：" + future.get() + ",用时：" + (System.currentTimeMillis() - start2) + "ms.");

        }
    }
}
